﻿using Modbus.Device;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using DataConvertHelper;

namespace WindowsFormsApp1
{
    
    public partial class modbusControl : UserControl
    {
        ModbusIpMaster master = null;//modbus对象
        TcpClient tcpClient = null;//tcp客户端对象

        private CancellationTokenSource ushortctsRead;
        private CancellationTokenSource ushortctsWrite;

        private CancellationTokenSource shortctsRead;
        private CancellationTokenSource shortctsWrite;

        private CancellationTokenSource floatctsRead;
        private CancellationTokenSource floatctsWrite;

        private string ipAddress;
        private int port;
        private MainForm mainForm;

        public void SetMainForm(MainForm mainForm)
        {
            this.mainForm = mainForm;
        }

        public void InitializeModbusMaster()
        {
            try
            {
                if (mainForm != null)
                {
                    ipAddress = mainForm.GetIpAddress();
                    port = mainForm.GetPort();
                }

                if (tcpClient == null)
                {
                    tcpClient = new TcpClient();
                    tcpClient.Connect(ipAddress, port);
                    master = ModbusIpMaster.CreateIp(tcpClient);
                    Console.WriteLine(master);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"初始化 Modbus 主对象失败: {ex.Message}");
            }
        }
        public modbusControl()
        {
            InitializeComponent();
        }

       
        private void modbusControl_Load(object sender, EventArgs e)
        {
            cmddatatype.SelectedIndex = 0;
            cboReadTypes.SelectedIndex = 2;

            cmddatatype2.SelectedIndex = 0;
            cboWriteTypes.SelectedIndex = 3;

            // 确保初始化 Modbus 主对象
            

        }

        
        private void cboWriteTypes_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        private void myread_Click(object sender, EventArgs e)
        {
            
            // 确保 master 对象已初始化
            if (master == null)
            {
                MessageBox.Show("Modbus 主对象未初始化。");
                return;
            }
            // 停止所有异步任务
            ushortctsRead?.Cancel();
            shortctsRead?.Cancel();
            floatctsRead?.Cancel();
            // 由于NModbus4读取到寄存器的数据都是ushort类型
            //功能码
            string readType = cboReadTypes.Text.Trim();
            //从站地址
            byte slaveAddr = byte.Parse(txtRSlaveId.Text.Trim());
            //开始地址
            ushort startAddr = ushort.Parse(txtRStartAddress.Text.Trim());
            //读取数量
            ushort readCount = ushort.Parse(txtRCount.Text.Trim());
            switch (readType)
            {
                case "读线圈":
                    bool[] blVals = master.ReadCoils(slaveAddr, startAddr, readCount);
                    txtReadDatas1.Text = string.Join(",", blVals.Select(b => b ? "1" : "0"));
                    break;
                case "读输入线圈":
                    bool[] blInputVals = master.ReadInputs(slaveAddr, startAddr, readCount);
                    txtReadDatas1.Text = string.Join(",", blInputVals.Select(b => b ? "1" : "0"));
                    break;
                case "读保持寄存器":
                    //情况1：ushort到ushort类型：即读取无符号的整数，如23，89，处理方法是：原封不动
                    //ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, startAddr, readCount);
                    //txtReadDatas.Text = string.Join(",", uDatas);
                    //功能码
                    string dataType = cmddatatype.Text.Trim();
                    Console.WriteLine($"数据类型：{dataType}");
                    switch (dataType)
                    {
                        case "ushort":
                            //利用token循环读取
                            ushortctsRead = new CancellationTokenSource();
                            Task.Run(new Action(() =>
                            {
                                ReadUshortFromPLC(slaveAddr, startAddr, readCount);
                            }), ushortctsRead.Token);
                            break;
                        case "short":
                            //利用token循环读取
                            shortctsRead = new CancellationTokenSource();
                            Task.Run(new Action(() =>
                            {
                                ReadShortFromPLC(slaveAddr, startAddr, readCount);
                            }), shortctsRead.Token);
                            break;
                        case "float":
                            //利用token循环读取
                            floatctsRead = new CancellationTokenSource();
                            Task.Run(new Action(() =>
                            {
                                ReadFloatFromPLC(slaveAddr, startAddr, readCount);
                            }), floatctsRead.Token);
                            break;
                    }
                    break;
                case "读输入寄存器":
                    const int maxRetries = 3;
                    ushort[] uDatas1 = null;
                    for (int attempt = 0; attempt < maxRetries; attempt++)
                    {
                        try
                        {
                            uDatas1 = master.ReadInputRegisters(slaveAddr, startAddr, readCount);
                            Console.WriteLine($"读输入寄存器uDatas1：{uDatas1}");
                            Console.WriteLine($"读ReadInputRegisters：{master.ReadInputRegisters(slaveAddr, startAddr, readCount)}");
                            break; // 如果成功，则跳出循环
                        }
                        catch (IOException ex)
                        {
                            // 记录错误 (ex.Message)
                            if (attempt == maxRetries - 1)
                            {
                                throw; // 如果达到最大重试次数，则重新抛出异常
                            }
                        }
                    }
                    //ushort[] uDatas1 = master.ReadInputRegisters(slaveAddr, startAddr, readCount);
                    this.Invoke((Action)(() =>
                    {
                        txtReadDatas1.Text = string.Join(",", uDatas1);
                    }));

                    Console.WriteLine($"读输入寄存器uDatas1：{txtReadDatas1.Text}");
                    break;

            }
        }

        /// <summary>
        /// 循环读取plc数据，ushort类型的
        /// </summary>
        /// <param name="slaveAddr">从站地址</param>
        /// <param name="startAddr">开始地址</param>
        /// <param name="readCount">读取数量</param>
        private void ReadUshortFromPLC(byte slaveAddr, ushort startAddr, ushort readCount)
        {
            // 确保 master 对象已初始化
            if (master == null)
            {
                MessageBox.Show("Modbus 主对象未初始化。");
                return;
            }
            while (!ushortctsRead.IsCancellationRequested)
            {
                try
                {
                    ushort[] uDatas = master.ReadHoldingRegisters(startAddr, readCount);
                    //ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, startAddr, readCount);
                    if (uDatas.Length == readCount)
                    {
                        //异步更新
                        if (txtReadDatas1.InvokeRequired)
                        {
                            txtReadDatas1.BeginInvoke(new Action(() =>
                            {
                                txtReadDatas1.Text = string.Join(",", uDatas);
                            }));
                        }
                        Thread.Sleep(4000);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    ushortctsRead.Cancel();
                }
            }
        }

        /// <summary>
        /// 循环读取plc数据，short类型的
        /// </summary>
        /// <param name="slaveAddr">从站地址</param>
        /// <param name="startAddr">开始地址</param>
        /// <param name="readCount">读取数量</param>
        private void ReadShortFromPLC(byte slaveAddr, ushort startAddr, ushort readCount)
        {
            // 确保 master 对象已初始化
            if (master == null)
            {
                MessageBox.Show("Modbus 主对象未初始化。");
                return;
            }
            while (!shortctsRead.IsCancellationRequested)
            {
                try
                {
                    ushort[] uDatas = master.ReadHoldingRegisters(startAddr, readCount);
                    //ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, startAddr, readCount);
                    byte[] y = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);
                    short[] shorts = ShortLib.GetShortArrayFromByteArray(y);
                    if (uDatas.Length == readCount)
                    {
                        //异步更新
                        if (txtReadDatas2.InvokeRequired)
                        {
                            txtReadDatas2.BeginInvoke(new Action(() =>
                            {
                                txtReadDatas2.Text = string.Join(",", shorts);
                            }));
                        }
                        Thread.Sleep(4000);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    shortctsRead.Cancel();
                }
            }
        }

        /// <summary>
        /// 循环读取plc数据，float类型的
        /// </summary>
        /// <param name="slaveAddr"></param>
        /// <param name="startAddr"></param>
        /// <param name="readCount"></param>
        private void ReadFloatFromPLC(byte slaveAddr, ushort startAddr, ushort readCount)
        {
            // 确保 master 对象已初始化
            if (master == null)
            {
                MessageBox.Show("Modbus 主对象未初始化。");
                return;
            }
            while (!floatctsRead.IsCancellationRequested)
            {
                try
                {
                    ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, startAddr, readCount);
                    byte[] t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);
                    float[] floats = FloatLib.GetFloatArrayFromByteArray(t);
                    if (uDatas.Length == readCount)
                    {
                        //异步更新
                        if (txtReadDatas3.InvokeRequired)
                        {
                            txtReadDatas3.BeginInvoke(new Action(() =>
                            {
                                txtReadDatas3.Text = string.Join(",", floats);
                            }));
                        }
                    }
                    Thread.Sleep(4000);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    floatctsRead.Cancel();
                }
            }
        }

        private void btnWrite_Click(object sender, EventArgs e)
        {
            
            // 确保 master 对象已初始化
            if (master == null)
            {
                MessageBox.Show("Modbus 主对象未初始化。");
                return;
            }
            //功能码
            string writeType = cboWriteTypes.Text.Trim();
            //从站地址
            byte slaveAddr = byte.Parse(txtWSlaveId.Text.Trim());
            //开始地址
            ushort startAddr = ushort.Parse(txtWStartAddress.Text.Trim());
            //数量
            //实际数量
            string objWriteVals = "";
            string dataType = cmddatatype2.Text.Trim();
            switch (dataType)
            {
                case "ushort":
                    objWriteVals = txtWriteDatas1.Text.Trim();
                    break;
                case "short":
                    objWriteVals = txtWriteDatas2.Text.Trim();
                    break;
                case "float":
                    objWriteVals = txtWriteDatas3.Text.Trim();
                    break;
            }
            ushort writeCount = ushort.Parse(txtWCount.Text.Trim());
            ushort objWCount = (ushort)objWriteVals.Split(',').Length;
            //实际数量与要求数量不一致，不允许操作
            if (writeCount != objWCount)
            {
                MessageBox.Show("写入值的数量不正确!");
                return;
            }
            string vals = objWriteVals;
            switch (writeType)
            {
                case "写单线圈":
                    bool blVal = vals == "1" ? true : false;
                    try
                    {
                        master.WriteSingleCoil(slaveAddr, startAddr, blVal);
                        MessageBox.Show("【单线圈】写入成功!");
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                    break;
                case "写单保持寄存器":
                    ushort uVal01 = ushort.Parse(vals);
                    try
                    {
                        master.WriteSingleRegister(slaveAddr, startAddr, uVal01);
                        MessageBox.Show("【单保持寄存器】写入成功!");
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                    break;
                case "写多线圈":
                    bool[] blVals = vals.Split(',').Select(s => s == "1" ? true : false).ToArray();//bool数组
                    try
                    {
                        master.WriteMultipleCoils(slaveAddr, startAddr, blVals);
                        MessageBox.Show("【多线圈】写入成功!");
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                    break;
                case "写多保持寄存器":
                    try
                    {
                        //功能码 
                        switch (dataType)
                        {
                            case "ushort":
                                const int maxRetries = 3;
                                ushort[] uVals01 = null;
                                for (int attempt = 0; attempt < maxRetries; attempt++)
                                {
                                    try
                                    {
                                        uVals01 = vals.Split(',').Select(s => ushort.Parse(s)).ToArray();
                                        break; // 如果成功，则跳出循环
                                    }
                                    catch (IOException ex)
                                    {
                                        // 记录错误 (ex.Message)
                                        if (attempt == maxRetries - 1)
                                        {
                                            throw; // 如果达到最大重试次数，则重新抛出异常
                                        }
                                    }
                                }
                                ////情况1：写入无符号的整数，即写入ushort数据，如写入33，44
                                //ushort[] uVals01 = vals.Split(',').Select(s => ushort.Parse(s)).ToArray();
                                master.WriteMultipleRegisters(startAddr, uVals01);

                                break;
                            case "short":
                                //情况2：写入有符号的整数，即写入short数据，如写入-133，-65，98等，处理方法是：short[]=>byte[]=>ushort[]，情况2包括了情况1 
                                short[] uVals02 = vals.Split(',').Select(s => short.Parse(s)).ToArray();
                                byte[] y2 = ByteArrayLib.GetByteArrayFromShortArray(uVals02);
                                ushort[] ushorts2 = UShortLib.GetUShortArrayFromByteArray(y2);
                                master.WriteMultipleRegisters(startAddr, ushorts2);
                                MessageBox.Show("【short类型数据】写入成功!");
                                break;
                            case "float":
                                //情况3：写入有符号的小数，即写入float数据，如写入-6.3，-2.65，56.893，51，-465等，处理方法是：float[]=>byte[]=>ushort[]，情况3包括了情况2和情况1 
                                float[] uVals03 = vals.Split(',').Select(s => float.Parse(s)).ToArray();
                                byte[] y3 = ByteArrayLib.GetByteArrayFromFloatArray(uVals03);
                                ushort[] ushorts3 = UShortLib.GetUShortArrayFromByteArray(y3);
                                master.WriteMultipleRegisters(startAddr, ushorts3);
                                MessageBox.Show("【float类型数据】写入成功!");
                                break;
                        }



                        ////情况2：写入有符号的整数，即写入short数据，如写入-133，-65，98等，处理方法是：short[]=>byte[]=>ushort[]，情况2包括了情况1 
                        //short[] uVals02 = vals.Split(',').Select(s => short.Parse(s)).ToArray();
                        //byte[] y = ByteArrayLib.GetByteArrayFromShortArray(uVals02);
                        //ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);
                        //master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);

                        ////情况3：写入有符号的小数，即写入float数据，如写入-6.3，-2.65，56.893，51，-465等，处理方法是：float[]=>byte[]=>ushort[]，情况3包括了情况2和情况1 
                        //float[] uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();
                        //byte[] y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);
                        //ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);
                        //master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);

                        MessageBox.Show("【多保持寄存器】写入成功!");
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                    }
                    break;
            }
        }

        private void gpRead_Enter(object sender, EventArgs e)
        {

        }
    }
}
